#include <iostream>
#include <exception>
#include <thread>
#include <mutex>
#include <future>
#include <fstream>
#include "Point.h"
#include "Quadrature.h"
#include "Poisson.h"
#include "Multigrid.h"
#include "Factory.h"

using namespace std;

typedef SingletonHolder<Factory<AbstractMultigrid, tuple<string, string, string, string> > > MultigridFactory;
typedef SingletonHolder<Factory<TriElement, string> > ElementFactory;
typedef SingletonHolder<Factory<AbstractPoisson, string> > PoissonFactory;

void RunOneMultigrid(string Element, string Cycle, string Restriction, string Interpolation);
void RunOnePoisson(string Element, size_t n);
void RunMultigrid();
void RunPoisson();

mutex mf, ef, pf;

int main()
{
	try {
		Register();
		//RunMultigrid();
		RunPoisson();
	}
	catch (exception& e) {
		cerr << e.what() << endl;
	}

	return 0;
}

void RunOneMultigrid(string Element, string Cycle, string Restriction, string Interpolation)
{
	size_t n = 64;
	valarray<Point<2> > domain({{1, 2}, {1, 2}});
	unique_lock<mutex> lock1(mf);	
	shared_ptr<AbstractMultigrid> multigrid(MultigridFactory::Instance().CreateObject(make_tuple(Element, Cycle, Restriction, Interpolation)));
	mf.unlock();
	unique_lock<mutex> lock2(ef);
	shared_ptr<TriElement> element(ElementFactory::Instance().CreateObject(Element));
	ef.unlock();
	unique_lock<mutex> lock3(pf);
	shared_ptr<AbstractPoisson> poisson(PoissonFactory::Instance().CreateObject(Element));
	pf.unlock();
	Vector initial_guess(element->GetNodeNum(n));
	poisson->SetParams(n, domain);

	if (Cycle == "VCycle")
		multigrid->SetParams(n, initial_guess, domain, 10, 1e-16, n, n); 
	else
		multigrid->SetParams(n, initial_guess, domain, 1, 1e-16, n, n);
	double error = poisson->L2Error(multigrid->Solve());
	cout << "n = " << n << ", 多重网格 " << Element << ", " << Cycle << ", " << Restriction << ", "
		 << Interpolation << " 的L2误差为 " << error << endl;
}

void RunMultigrid()
{
	ifstream input("../data/input_multigrid.txt", ios::in);		
	string Element, Cycle, Restriction, Interpolation;
	thread thread_array[20];
	size_t id = 0;
	while (input >> Element >> Cycle >> Restriction >> Interpolation)
		thread_array[id++] = thread(RunOneMultigrid, Element, Cycle, Restriction, Interpolation);
	input.close();
	for (size_t i = 0; i < id; ++i)
		thread_array[i].join();
}

void RunOnePoisson(string Element, size_t n)
{	
	valarray<Point<2> > domain({{1, 2}, {1, 2}});
	unique_lock<mutex> lock3(pf);
	shared_ptr<AbstractPoisson> poisson(PoissonFactory::Instance().CreateObject(Element));
	pf.unlock();

	poisson->SetParams(n, domain);
	const Vector &solution = poisson->Solve(); 
	double error = poisson->L2Error(solution);
	cout << "二维泊松方程, " << Element << ", n = " << n << ", L2误差为 " << error << endl;
	poisson->SaveToFile(solution, "../data/" + Element + "_" + to_string(n) + ".txt");
}

void RunPoisson()
{
	thread thread_array[100];
	size_t id = 0;
	for (size_t n = 64; n <= 64; n *= 2) {
		thread_array[id++] = thread(RunOnePoisson, "P1Element", n);
		thread_array[id++] = thread(RunOnePoisson, "P2Element", n);
	}
	for (size_t i = 0; i < id; ++i)
		thread_array[i].join();
}
